/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package action;

import collection.Categories;
import collection.Products;
import dao.CategoryDAO;
import dao.ProductDAO;
import dao.UserDAO;
import dto.Category;
import dto.Product;
import dto_pakinfo.User;
import dto_pakinfo.Users;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.InputStream;
import java.util.List;
import java.io.IOException;
import java.io.StringBufferInputStream;
import java.io.StringReader;
import javax.servlet.http.HttpServletRequest;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.hibernate.HibernateException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import util.ServerValidation;
import util.StringUtil;
import util.XMLUtil;

/**
 *
 * @author Thinh
 */
public class AdminController extends BaseController implements ServletRequestAware {

    private UserDAO userDAO;
    private List<User> users;
    private JAXBContext jaxbContext;
    private String filePath;
    private String schemaFilePath;
    private String userId;
    private Users userUtility;
    Log log = LogFactory.getLog(AdminController.class);
    private HttpServletRequest servletRequest;
    private InputStream inputStream;
    private int categoryId;
    private String nonadminUserFilePath;
    private String userfilePath;
    private int parentId;
    private Category category;
    private List<Category> parentCategories;
    private String resultStream;
    private String updatedUser;
    private String styleSheetPath;

    public InputStream getInputStream() {
        return inputStream;
    }

    public AdminController() {

        super();
        userDAO = new UserDAO(User.class);
        filePath = System.getProperty("rootPath") + StringUtil.XML_FOLDER_PATH;
        nonadminUserFilePath = filePath + "nonadminusers.xml";
        userfilePath = filePath + "users.xml";
        schemaFilePath = System.getProperty("rootPath") + StringUtil.XML_FOLDER_PATH + "user.xsd";
        styleSheetPath = System.getProperty("rootPath") + StringUtil.XSL_FOLDER_PATH;
        userUtility = new Users();
    }

    public String load() {
        return SUCCESS;
    }

    public String load_user() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isCustomer()) {
            return StringUtil.INVALID_ROLE;
        } else {
            try {
                File nonuserxmlFile = new File(nonadminUserFilePath);
                if (!nonuserxmlFile.exists()) {
                    jaxbContext = JAXBContext.newInstance(Users.class);

                    getUserUtility().getUserIsNotAdmin(userfilePath);

                    XMLUtil.marshalObject(jaxbContext, getUserUtility(), nonadminUserFilePath);
                }

            } catch (Exception ex) {
                log.error("AdminController.load_user()", ex);
                System.out.println("AdminController.load_user(): " + ex.toString());
                return ERROR;
            }
            return SUCCESS;
        }
    }

    public String delete_user() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isCustomer()) {
            return StringUtil.INVALID_ROLE;
        } else {
            try {
                //Update user in db

                Document node = XMLUtil.parserDOMXML(nonadminUserFilePath);

                getUserUtility().deleteUserNode(node, userId);

                int iUserId = Integer.parseInt(userId);
                userDAO.openSession();
                if (userDAO.deleteUser(iUserId)) {
                    System.out.println("[Delete in db sucessfull] : " + iUserId);
                    userDAO.closeSession();
                    XMLUtil.writeXML(node, nonadminUserFilePath);
                    //Return OK when sucessfull
                    inputStream = new ByteArrayInputStream(StringUtil.OK.getBytes());
                } else {
                    inputStream = new ByteArrayInputStream(StringUtil.CANCELLED.getBytes());
                }

                return SUCCESS;
            } catch (Exception ex) {
                log.error("AdminController.delete_user()", ex);
                System.out.println("AdminController.delete_user()" + ex);

            } finally {
                userDAO.closeSession();
            }
            return ERROR;
        }
    }

    public String load_categories() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isCustomer()) {
            return StringUtil.INVALID_ROLE;
        } else {
            return SUCCESS;
        }
    }

    /**
     * Get information from category which is updated by categoryId then show to update_category.jsp page.
     * Using XPath to get information of category that need to be update
     * If that category already have a parent, then unmarshal categories.xml to initialize value for Categories object.
     * @return
     */
    public String updateCategory() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isCustomer()) {
            return StringUtil.INVALID_ROLE;
        } else {
            try {
                String filepath = System.getProperty("rootPath") + StringUtil.XML_FOLDER_PATH + "categories.xml";
                Document doc = XMLUtil.parserDOMXML(filepath);
                XPath xpath = XPathFactory.newInstance().newXPath();
                XPathExpression expr = xpath.compile("//category[@id=" + categoryId + "]");
                Object result = expr.evaluate(doc, XPathConstants.NODE);

                if (result == null) {
                    return ERROR;
                }

                Node node = (Node) result;
                NodeList children = node.getChildNodes();
                category = new Category();
                category.setId(categoryId);
                for (int i = 0; i < children.getLength(); i++) {
                    Node tmp = children.item(i);
                    if (tmp.getNodeName().equals("name")) { // element name
                        category.setName(tmp.getTextContent().trim());
                    } else if (tmp.getNodeName().equals("deleted")) { // element deleted
                        if (tmp.getTextContent().trim().equals("true")) {
                            category.setIsDeleted(true);
                        } else {
                            category.setIsDeleted(false);
                        }
                    } else if (tmp.getNodeName().equals("parent")) { // element parent, this is sub category
                        category.setCategory(new Category());
                        String sId = tmp.getAttributes().getNamedItem("id").getNodeValue();
                        category.getCategory().setId(Integer.parseInt(sId));

                        NodeList childnodes = tmp.getChildNodes(); // set value for parent category
                        for (int j = 0; j < childnodes.getLength(); j++) {
                            Node tmp2 = childnodes.item(j);
                            if (tmp2.getNodeName().equals("name")) { // element name
                                category.getCategory().setName(tmp2.getTextContent().trim());
                            } else if (tmp2.getNodeName().equals("deleted")) { // element deleted
                                if (tmp2.getTextContent().trim().equals("true")) {
                                    category.getCategory().setIsDeleted(true);
                                } else {
                                    category.getCategory().setIsDeleted(false);
                                }
                            }
                        }
                    }
                }

                if (category.getCategory() != null) { // update sub category, so can update name, main category of this sub
                    parentCategories = new ArrayList<Category>();
                    JAXBContext context = JAXBContext.newInstance(Categories.class);
                    Unmarshaller u = context.createUnmarshaller();
                    Categories obj = (Categories) u.unmarshal(doc);
                    parentCategories = obj.getCategories();
                }



                return SUCCESS;
            } catch (Exception ex) {
                log.error("AdminController.updateCategory()", ex);
                return ERROR;
            }


        }
    }

    /**
     * Save updated category: save to DOM and write to xml file, then save to database.
     * @return
     */
    public String submit_updated_category() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isCustomer()) {
            return StringUtil.INVALID_ROLE;
        } else {
            CategoryDAO cateDAO = new CategoryDAO(Category.class);
            try {
                // update DOM
                filePath = System.getProperty("rootPath") + StringUtil.XML_FOLDER_PATH + "categories.xml";
                Document doc = XMLUtil.parserDOMXML(filePath);
                XPath xpath = XPathFactory.newInstance().newXPath();
                XPathExpression expr = xpath.compile("//*[@id=" + categoryId + "]");
                NodeList categoryNodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);

                cateDAO.openSession();
                Category obj = cateDAO.get(categoryId);
                Node node;
                for (int a = 0; a < categoryNodes.getLength(); a++) {
                    node = categoryNodes.item(a);
                    NodeList childNodes = node.getChildNodes();
                    for (int i = 0; i < childNodes.getLength(); i++) {
                        Node tmp = childNodes.item(i);
                        System.out.println("tmp node: " + tmp.getNodeName());
                        if (tmp.getNodeName().trim().equals("name")) { // update name for category node
                            tmp.setTextContent(category.getName());
                            System.out.println("----- Updated node: " + tmp.getTextContent());
                            obj.setName(category.getName());
                        } else if (tmp.getNodeName().trim().equals("parent")) { // for sub category
                            // category updated with no parent, option = --- NONE ---
                            // just find that category node by id then remove <parent></parent> node
                            if (parentId == -1) {
                                // remove child node, tmp = <parent> tag, node = <category> tag.
                                node.removeChild(tmp);
                                obj.setCategory(null);
                            } else if (parentId > 0) {
                                // update id attribute of <parent id=""></parent> to new parent id
                                tmp.getAttributes().getNamedItem("id").setNodeValue(String.valueOf(parentId));
                                Category newCategory = cateDAO.get(parentId);
                                obj.setCategory(newCategory);

                                // update value for child tag of parent
                                NodeList childrenOfParent = tmp.getChildNodes();
                                for (int j = 0; j < childrenOfParent.getLength(); j++) {
                                    Node tmp2 = childrenOfParent.item(j);
                                    if (tmp2.getNodeName().trim().equals("name")) {
                                        tmp2.setTextContent(newCategory.getName());
                                    } else if (tmp2.getNodeName().trim().equals("deleted")) {
                                        tmp2.setTextContent(String.valueOf(newCategory.isIsDeleted()));
                                    }
                                }
                            } else { // update parent name for other sub category
                                NodeList childrenOfParent = tmp.getChildNodes();
                                for (int j = 0; j < childrenOfParent.getLength(); j++) {
                                    Node tmp2 = childrenOfParent.item(j);
                                    if (tmp2.getNodeName().trim().equals("name")) {
                                        tmp2.setTextContent(category.getName());
                                    }
                                }
                            }
                        }
                    }
                }

                // save to db
                cateDAO.update(obj);

                // write dom to file
                XMLUtil.writeXML(doc, filePath);

                // update products.xml by marshal products
                ProductDAO productDAO = new ProductDAO(Product.class);
                productDAO.openSession();
                List<Product> products = productDAO.all();
                productDAO.closeSession();
                filePath = System.getProperty("rootPath") + util.StringUtil.XML_FOLDER_PATH + "products.xml";
                Products collection = new Products(products);
                jaxbContext = JAXBContext.newInstance(Products.class);
                XMLUtil.marshalObject(jaxbContext, collection, filePath);

                return SUCCESS;
            } catch (Exception ex) {
                log.error("AdminController.submit_updated_category()", ex);
                if (cateDAO != null && cateDAO.getSession().isOpen() && cateDAO.getSession().getTransaction().isActive()) {
                    cateDAO.getSession().getTransaction().rollback();
                }
                return ERROR;
            } finally {
                if (cateDAO != null) {
                    try {
                        cateDAO.closeSession();
                    } catch (HibernateException ex) {
                        log.error("Can't close exception in AdminController.submit_updated_category()", ex);
                    }
                }
            }

        }

    }

    /**
     * Add new category using AJAX
     * @return DOM tree of category to client.
     */
    public String addcategoryAJAX() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isCustomer()) {
            return StringUtil.INVALID_ROLE;
        } else {
            CategoryDAO cateDAO = new CategoryDAO(Category.class);
            try {
                String name = servletRequest.getParameter("categoryName");
                String sParentId = servletRequest.getParameter("parentId");
                if (name == null || sParentId == null) {
                    return ERROR;
                }
                parentId = Integer.parseInt(sParentId);

                cateDAO.openSession();
                category = new Category();
                category.setName(name);
                // if new category have parent, set parent
                if (parentId != -1) {
                    Category parent = cateDAO.get(parentId);
                    category.setCategory(parent);
                }
                cateDAO.save(category);

                // update DOM
                filePath = System.getProperty("rootPath") + StringUtil.XML_FOLDER_PATH + "categories.xml";

                // add new category node to categories.xml
                Document doc = XMLUtil.parserDOMXML(filePath);
                Element cate = doc.createElement("category");
                cate.setAttribute("id", String.valueOf(category.getId()));
                Element cateName = doc.createElement("name");
                cateName.setTextContent(category.getName());
                cate.appendChild(cateName);
                Element deleted = doc.createElement("deleted");
                deleted.setTextContent(String.valueOf(category.isIsDeleted()));
                cate.appendChild(deleted);

                // add parent tag
                if (category.getCategory() != null) {
                    Element parent = doc.createElement("parent");
                    parent.setAttribute("id", String.valueOf(category.getCategory().getId()));
                    Element parentName = doc.createElement("name");
                    parentName.setTextContent(category.getCategory().getName());
                    parent.appendChild(parentName);
                    Element parentDeleted = doc.createElement("deleted");
                    parentDeleted.setTextContent(String.valueOf(category.getCategory().isIsDeleted()));
                    parent.appendChild(parentDeleted);

                    cate.appendChild(parent);
                }

                // add new category node to parent
                NodeList categories = doc.getElementsByTagName("categories");
                categories.item(0).appendChild(cate);

                // write added node to xml file
                XMLUtil.writeXML(doc, filePath);

                // marshall category to xml and respone to client
                ByteArrayOutputStream os = new ByteArrayOutputStream();

                JAXBContext context = JAXBContext.newInstance(category.getClass());
                XMLUtil.marshalObject(context, category, os);
                inputStream = new ByteArrayInputStream(os.toByteArray());

                return SUCCESS;
            } catch (Exception ex) {
                inputStream = new ByteArrayInputStream("<result>fail</result>".getBytes());
                log.error("AdminController.addcategoryAJAX()", ex);
                if (cateDAO.getSession().isOpen() && cateDAO.getSession().getTransaction().isActive()) {
                    cateDAO.getSession().getTransaction().rollback();
                }
                return ERROR;
            } finally {
                try {
                    cateDAO.closeSession();
                } catch (HibernateException ex) {
                    log.error("Can't close exception in AdminController.addcategoryAJAX()", ex);
                }
            }
        }
    }

    /**
     * Delete a category using AJAX request from client.
     * @return string of deleted category to client, ex: "1 5 12..".
     */
    public String deleteCategoryAjax() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isCustomer()) {
            return StringUtil.INVALID_ROLE;
        } else {
            CategoryDAO cateDAO = new CategoryDAO(Category.class);
            try {

                cateDAO.openSession();
                String sId = servletRequest.getParameter("id");
                String isDeleted = servletRequest.getParameter("isDeleted");
                int id = Integer.parseInt(sId);
                boolean value = Boolean.parseBoolean(isDeleted.trim());
                cateDAO.updateDeletedStatus(id, value);

                // update categories.xml
                filePath = System.getProperty("rootPath") + util.StringUtil.XML_FOLDER_PATH + "categories.xml";
                Document doc = XMLUtil.parserDOMXML(filePath);
                XPath xpath = XPathFactory.newInstance().newXPath();
                XPathExpression expr = xpath.compile("//*[@id=" + id + "]");
                NodeList categoryNodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);

                Categories cates = new Categories();
//            cates.updateDeletedCategory(doc, id, value);
                cates.updateDeletedCategory(categoryNodes, value);

                // set value response for inputStream
                inputStream = new ByteArrayInputStream(cates.getDeletedIdString().getBytes());

                XMLUtil.writeXML(doc, filePath);

                return SUCCESS;

            } catch (Exception ex) {
                inputStream = new ByteArrayInputStream("error".getBytes());
                if (cateDAO != null && cateDAO.getSession().isOpen() && cateDAO.getSession().getTransaction().isActive()) {
                    cateDAO.getSession().getTransaction().rollback();
                }
                log.error("AdminController.deleteCategoryAjax()", ex);
                // rollback
                return ERROR;
            } finally {
                if (cateDAO != null) {
                    try {
                        cateDAO.closeSession();
                    } catch (HibernateException ex) {
                        log.error("Can't close exception in OrderController.submited_checkout()", ex);
                    }
                }
            }
        }

    }

    /**
     * @return the _users
     */
    public List<User> getUsers() {
        return users;
    }

    /**
     * @param users the _users to set
     */
    public void setUsers(List<User> users) {
        this.users = users;
    }

    public void setServletRequest(HttpServletRequest hsr) {
        this.servletRequest = hsr;
    }

    public String getUserId() {
        return userId;
    }

    /**
     * @param userId the userId to set
     */
    public void setUserId(String userId) {
        this.userId = userId;
    }

    /**
     * @return the categoryId
     */
    public int getCategoryId() {
        return categoryId;
    }

    /**
     * @param categoryId the categoryId to set
     */
    public void setCategoryId(int categoryId) {
        this.categoryId = categoryId;
    }

    /**
     * @return the category
     */
    public Category getCategory() {
        return category;
    }

    /**
     * @param category the category to set
     */
    public void setCategory(Category category) {
        this.category = category;
    }

    /**
     * @return the parentCategories
     */
    public List<Category> getParentCategories() {
        return parentCategories;
    }

    /**
     * @param parentCategories the parentCategories to set
     */
    public void setParentCategories(List<Category> parentCategories) {
        this.parentCategories = parentCategories;
    }

    /**
     * @return the parentId
     */
    public int getParentId() {
        return parentId;
    }

    /**
     * @param parentId the parentId to set
     */
    public void setParentId(int parentId) {
        this.parentId = parentId;
    }

    /**
     * Get UserXML for sending to client
     * Created Date: 29/3/2013
     * @return String
     */
    public String getUserXML() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else {
            try {
//                File f = new File(nonadminUserFilePath);
                File f = new File(nonadminUserFilePath);
                if (!f.exists()) {
                    jaxbContext = JAXBContext.newInstance(Users.class);

                    getUserUtility().getUserIsNotAdmin(userfilePath);

                    XMLUtil.marshalObject(jaxbContext, getUserUtility(), nonadminUserFilePath);
                }
                inputStream = new FileInputStream(f);
            } catch (Exception ex) {
                Logger.getLogger(AdminController.class.getName()).log(Level.SEVERE, null, ex);
                return ERROR;
            }
            return SUCCESS;
        }
    }

    public String getUserXSL() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else if (ServerValidation.isCustomer()) {
            return StringUtil.INVALID_ROLE;
        } else {
            try {
                String userXSL = styleSheetPath + "userTable.xsl";
                File f = new File(userXSL);
                inputStream = new FileInputStream(f);
            } catch (FileNotFoundException ex) {
                Logger.getLogger(AdminController.class.getName()).log(Level.SEVERE, null, ex);
                return ERROR;
            }
            return SUCCESS;
        }
    }

    /**
     * Receive xml doc from client
     * Created Date : 31/3/2013
     * @return String
     */
    public String receiveXMLDoc() {
        if (ServerValidation.isGuest()) {
            return LOGIN;
        } else {
            if (resultStream != null) {
                try {
                    String schemaFilePath = filePath + "user.xsd";

                    //Validate this inputStream
                    SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
                    Schema schema = schemaFactory.newSchema(new File(schemaFilePath));

                    Validator validator = schema.newValidator();

                    InputSource source = new InputSource(new StringReader(resultStream));
                    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                    dbf.setNamespaceAware(true);
                    DocumentBuilder db = dbf.newDocumentBuilder();
                    Document doc = db.parse(source);

                    validator.validate(new DOMSource(doc));

                    //Unmarshall to save object to database
                    JAXBContext context = JAXBContext.newInstance(Users.class);
                    Users listUpdatedUsers = (Users) XMLUtil.unmarshallObject(context, doc, schemaFilePath);

                    //Receive id's users updated
                    String[] idUpdatedUser = updatedUser.split(",");
                    for (int i = 0; i < idUpdatedUser.length; ++i) {
                        int updatedUserId = Integer.parseInt(idUpdatedUser[i]);
                        User updatedUser = listUpdatedUsers.getUserById(updatedUserId);
                        userDAO.openSession();
                        userDAO.updateUser(updatedUser);
                        userDAO.closeSession();
                    }

                    //Write to xml
                    XMLUtil.writeXML(doc, nonadminUserFilePath);
                    inputStream = new ByteArrayInputStream(StringUtil.OK.getBytes());
                    return SUCCESS;

                } catch (TransformerConfigurationException ex) {
                    Logger.getLogger(AdminController.class.getName()).log(Level.SEVERE, null, ex);
                } catch (FileNotFoundException ex) {
                    Logger.getLogger(AdminController.class.getName()).log(Level.SEVERE, null, ex);
                } catch (UnsupportedEncodingException ex) {
                    Logger.getLogger(AdminController.class.getName()).log(Level.SEVERE, null, ex);
                } catch (TransformerException ex) {
                    Logger.getLogger(AdminController.class.getName()).log(Level.SEVERE, null, ex);
                } catch (IOException ex) {
                    Logger.getLogger(AdminController.class.getName()).log(Level.SEVERE, null, ex);
                } catch (SAXException ex) {
                    System.out.println("XSD occurs errors: " + ex);
                    Logger.getLogger(AdminController.class.getName()).log(Level.SEVERE, null, ex);
                } catch (Exception ex) {
                    System.out.println("XSD occurs errors: " + ex);
                    Logger.getLogger(AdminController.class.getName()).log(Level.SEVERE, null, ex);
                }

            }
            return ERROR;
        }
    }

    /**
     * @return the resultStream
     */
    public String getResultStream() {
        return resultStream;
    }

    /**
     * @param resultStream the resultStream to set
     */
    public void setResultStream(String resultStream) {
        this.resultStream = resultStream;
    }

    /**
     * @return the updatedUser
     */
    public String getUpdatedUser() {
        return updatedUser;
    }

    /**
     * @param updatedUser the updatedUser to set
     */
    public void setUpdatedUser(String updatedUser) {
        this.updatedUser = updatedUser;
    }

    /**
     * @return the userUtility
     */
    public Users getUserUtility() {
        return userUtility;
    }

    /**
     * @param userUtility the userUtility to set
     */
    public void setUserUtility(Users userUtility) {
        this.userUtility = userUtility;
    }
}
